Unique's Blog

Vue 基础

2022-06-09 · 4547字 · 19 min read
🏷️  Vue

基于官方文档总结

[TOC]

阅读

模版语法

Vue允许声明式地将 DOM 绑定至底层组件实例的数据。

插值

  1. 文本插值

使用<span>Message: {{ msg }}</span>语法绑定组件实例中的对应的 property 的值。
使用v-once指令进行一次性插值

  1. 原始 HTML

    文本插值会解释为普通文本,如果需要输出 HTML,使用v-html 指令:

<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
<!--将整个v-html作用的标签 替换作为html内容-->
  1. 属性 Attribute

    用于动态修改标签属性的值,使用v-bind 指令

<img v-bind:src="img" alt="img" />
<img :src="img" alt="img" />

说明:如果绑定的值是nullundefined,那么该 attribute 将不会被包含在渲染的元素上。(bool 属性有所区别)

  1. 使用JS表达式

    对于所有的数据绑定,Vue 都提供了完全的 JavaScript 表达式支持(单个表达式)

{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>

事件中也可以使用JS表达式

指令

常见指令如下,有的指令可以接收参数(冒号后)

  • v-html

  • v-bind(可以缩写:

  • v-model 在表单<input><textarea><select>等元素上创建双向数据绑定。

  • v-if

  • v-for

  • v-on(可以缩写@

指令参数也是支持 JS 表达式,语法:

<a v-bind:[attributeName]="url"> ... </a>

data 和 methods

组件的data选项是函数,创建组件实例时返回一个对象,并以$data存储在组件实例中。

同时该对象的顶级属性 property 会直接通过组件示例暴露处理。

组件的methods选项为包含组件所需要的方法的对象,Vue 自动为其绑定了this,始终指向组件实例。定义方法时避免使用箭头函数

const app = Vue.createApp({
  data() {
    return { count: 4 }
  },
  methods: {
    increment() {
      // `this` 指向该组件实例
      this.count++
    }
  }
})

const vm = app.mount('#app')
console.log(vm.count) // => 4
vm.increment()
console.log(vm.count) // => 5

data 返回对象的原因:

组件是可复用的,通过函数局部作用域创建并返回的对象,每个组件实例是不同的,否则组件之间的数据可能干扰。

计算属性和侦听器

计算属性 computed

💡

对于包含响应式数据的复杂逻辑,应该使用计算属性

在配置对像中的computed对象中定义计算属性(其实是一个函数并返回结果)。使用时可以像普通属性一样将数据(计算属性的结果)绑定到模版中的计算属性。

好处:一次声明Vue可以知道该计算属性依赖的真实属性,当依赖属性发送改变时,依赖该计算属性的绑定都会更新。与使用方法相比,计算属性可以缓存结果

<div id="computed-basics">
  <span>{{ computedMsg }}</span>
</div>
Vue.createApp({
	data(){...}
	computed: {
    // 计算属性
    computedMsg() {
      return this.msg.split('').reverse().join('');
    },
  },
}).mount('#computed-basics')

侦听器 watch

watch选项提供类一个更加通用方式来响应数据的变化。用于执行异步操作,或者开销比较大的操作。

...
watch: {
  // 每当 message 发生变化时,该函数将会执行
  message(newValue, oldValue) {
		if(newValue.length()>oldValue.length()){
	     this.getNewMessage()
    }
  }
},
...

深度侦听

...
watch: {
  "user.name": {     // 使用字符串,仅仅单独侦听该对象的某个属性
		immediate: true, // 初始化时也会调用函数
		deep: true,      // 深度侦听,用于侦听对象中的属性变化
		handler: function(newValue){
			console.log(newValue);
		}
	}
},
...

样式绑定

操作元素的 class 列表和内联样式其实也是一种数据绑定,绑定元素的 attribute。

使用 v-bind 绑定class | style,表达式结果可以是字符串、对象以及数组

例如使用 data property 中的对象进行绑定:

<div :class="classObject"></div> <!-- v-bind:class 简写 -->
data() {
  return {
    classObject: {  // 作为绑定的对象
      active: true,
      'text-danger': false
    }
  }
}

或者可以绑定一个返回对象计算属性;数组和对象也可以结合使用。

内联样式绑定与之类似。CSS property 名用驼峰式或短横线分隔 (用引号括起来) 来命名。

渲染

条件渲染

  • v-if/v-else/v-else-if有条件的渲染元素/分组(使用<template>标签包裹)

  • v-show

使用v-if/v-else,v-if 中表达式返回 truthy 值的时候被渲染,否则渲染 v-else 元素

<h1 v-if="awesome">Vue is awesome!</h1> 
<h1 v-else>Oh no 😢</h1>

v-show的元素始终会被渲染并保留在 DOM 中。v-show只是简单地切换元素的displayCSS property。它不支持<template>元素,也不支持v-else

💡

v-if更多适用与一次渲染或者条件改变不频繁的场景,v-show可用于条件频繁改变场景

列表渲染

使用v-for进行列表渲染数组

<ul id="array-with-index">
	<!--(item, index) of items-->
  <li v-for="(item, index) in items" :key="item.id">   
    {{ index }} - {{ item.message }}
  </li>
</ul>

也可以渲染一个对象,第一个参数是对象键值,第二个参数是键名(可选的第三个参数 index)。注意遍历对象时使用的Object.keys()结果遍历。

注意:Vue 默认使用就地更新的策略,如果数据项顺序发生改变,并不会移动 DOM 元素来匹配数据项的位置顺序,只是就地更新每个元素。因此应该尽可能在使用v-for时提供:key作为标识来追踪该节点的身份(应该应用哪个数据项)。

Vue 将侦听的数组的变更方法进行了包裹,使用会自动触发视图的更新。

显示过滤/排序后的结果?

可以使用计算属性,不实际变更或重置原始数据,只是返回过滤/排序后的数组。或者在嵌套的 v-for 循环中使用 methods 。

  • v-for 中可以使用整数,<span v-for="n in 10" :key="n">{{ n }} </span>重复模版 10次

  • 利用<template>和v-for循环渲染一段包含多个元素的内容

v-for 与 v-if

推荐在同一元素上使用v-ifv-for。当它们处于同一节点,v-if的优先级比v-for更高,这意味着v-if将没有权限访问v-for里的变量。

推荐写法:使用<template>

<template v-for="todo in todos" :key="todo.name"> 
  <li v-if="!todo.isComplete">
    {{ todo.name }}
  </li>
</template>

事件处理

使用v-on指令 (通常缩写为@符号) 来监听 DOM 事件,并在触发事件时执行一些 JavaScript。用法为v-on:click="methodName"或使用快捷方式@click="methodName"

事件处理写法:

  • 直接内联 JS 代码

  • 定义 Methods 中的(事件处理)方法,直接绑定一个方法的名称

  • 在内联 JavaScript 语句中调用方法(12结合);用特殊变量$event表示原始 DOM event对象

事件修饰符:

  • .stop阻止事件继续冒泡

  • .prevent阻止默认行为,例如提交事件会重载页面

  • .once只能触发一次处理函数

  • .self只当在 event.target 是当前元素自身时触发处理函数

  • .capture内部元素触发的事件先在此处理

表单输入绑定

v-model指令在表单<input><textarea><select>元素上创建双向数据绑定

本质就是:绑定数据,并且监听用户的输入数据来更新对应的数据。

v-model不会在输入法组织文字过程中得到更新。使用input事件监听器和value绑定来替代v-model可以响应。

【会忽略表单元素 attr 上的初始值,可以在组件的 data 选项中声明其初始值】

<input v-model="message" placeholder="edit me" />
<p>Message is: {{ message }}</p>

常见表单用法:

https://v3.cn.vuejs.org/guide/forms.html#文本-text

v-model 修饰符

  • .lazy

v-model 默认每次input事件触发后将输入框的值与数据进行同步;使用 .lazy 修饰转为在change事件之后进行同步

  • .number

    将用户的输入值转为数值类型;当输入类型为text时这通常很有用,如果值无法被parseFloat()解析,则返回原始值

  • .trim

    自动过滤用户输入的首尾空白字符

组件

组件是带名称的可复用的实例。都接收相同的选项,例如datacomputedwatchmethods以及生命周期钩子等。

说明:为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册局部注册。

向子组件传递数据

使用 Prop

在组件上注册一些自定义 attribute 即 Prop,使用props选项来定义该组件可接受的 prop 列表。父组件通过该 prop 来向该子组件传值,该 property 的值可以在该组件的模板中访问。

传值,可以使用v-bind:来动态传值:

<div id="blog-posts-demo">
  <blog-post v-for="post in posts" :key="post.id" :title="post.title">
  </blog-post>
</div>

定义props简单方式是定义 prop 数组,也可以定对象来为 prop 指定验证要求:

export default {
  props: {
    title: {
      type: String,
      default: 'Hello !!!',
      required: true,
    }
  }
}

注意:对象/数组的默认值,必须从一个工厂函数返回

单向数据流

单向下行绑定,父级 prop 的更新会向下流动到子组件中,但是反过来则不行

向父组件传递数据

不能直接使用 Prop 方式传递,父组件可以监听子组件自定义的事件来接收传递的数据

子组件中:

// 在方法中定义触发的事件
methods:{
	sendParent(){
		// $emit 来触发事件
		this.$emit("sendMsg", this.msg);
	}
},
...

父组件中:

// 在子组件元素上监听事件
<template>
	<ChildComponent @sendMsg="getChildMsg"></ChildComponent>
</template>

模版引用(访问组件)

在 JavaScript 中直接访问子组件,可以使用refattribute 为子组件或 HTML 元素指定引用 ID:

<base-input ref="usernameInput"></base-input>

以 JS 方式访问元素(DOM),例如以编程的方式 focus 到这个 input 。

父组件直接访问子组件$refs

父组件中定义的(子组件)元素上使用ref="usernameInput"添加引用名称后,可以使用$refs.usernameInput来获取引用元素.

$refs只会在组件渲染完成之后生效,避免在模板或计算属性中访问

子组件访问父组件$parent

子组件中使用$parent可以获取父组件对象信息。

由于组件是复用的,从而父组件可能是不同的,不建议使用该方式获取数据。而建议使用 Prop 来传递数据。

访问根组件$root

插槽

类似与 HTML 中向一个标签传递值:

<alert-box>Something bad happened.</alert-box>

在组件中可以利用**<slot></slot>**来作为插入内容的占位符,在使用该组件元素时(使用双标签语法)可以进行填充。

<Component1><button>111</button></Component1>
<Component1><p>hello world!</p></Component1>

如何定义多个插槽?

具名插槽,定义 slot 时添加 name 属性,使用组件元素时使用<templat> 和 v-slot指令包裹每个插槽对应的内容:

<template v-slot:header>
    <h1>Here might be a page title</h1>
</template>

作用域插槽

为了某些场景(自定义渲染列表的方式),插槽的中数据内容可以由子组件提供,使用**v-slot:default="slotProps"** 来接收子组件定义插槽时传递的所有数据对象 slotProps

<template v-slot:default="slotProps">
<ul>
	<li v-for="item in slotProps.list" :key="item">{{item}}</li>
</ul>
</template>

跨级通信

如果组件层次结构较深,使用provide, inject,父组件可以作为其所有子组件的依赖提供者。

父组件有一个provide 选项来提供数据,子组件有一个inject 选项来开始使用这些数据。作用类似于 Prop 数据的传递。

// 祖先组件的 provide 选项
provide: {
    user: 'John Doe'
},

// 子组件使用 inject 选项
inject: ['user'],

注意:如果 provide 选项是一个对象,则不能直接访问组件实例的 property。

为了使用组件实例的 property,则 provide 选项需要改为返回对象的函数

响应性问题

即使这样,访问的 property 也不是响应性绑定,即祖先数据的变化不会反映在子组件 inject 中的 property。默认的 provide/inject 绑定不是响应式的。

resolve: 可以通过传递一个refproperty 或reactive对象给provide来改变这种行为

  • 响应式对象
provide(){
	return{
		obj: this.obj,  // 接收的是包含数据的对象(修改也是修改该对象中的数据)
	}
},
  • 箭头函数(响应式数据)
provide(){
	return{
		message: ()=>this.message, // 接收的是函数,调用才返回响应式数据
	}
},

子组件多次调用中,可以使用计算属性来缓存该结果。

  • 组合式 APIcomputed

生命周期

生命周期图示

使用:

  • 组件视图更新(绑定数据更新)会执行对应的beforeUpdate,updated函数

  • 组件的销毁和创建,会执行beforeUnmount,unmounted函数,例如使用 v-if 进行条件渲染。

组合式API

作用:将同一个逻辑关注点相关代码收集在一起。

setup选项是一个接收props/context的函数,在组件创建之前执行(可以代替 beforeCreate, created 生命周期钩子)。并且不能使用 this 来访问组件实例。

setup 返回的所有内容都暴露给组件的其余部分 (计算属性、方法、生命周期钩子等等) 以及组件的模板(模版中使用的引用变量会自动浅解包)。

setup 选项中定义的变量默认是非响应式的(修改不会影响视图),使用ref函数接收参数并将其包裹在一个带有valueproperty 的对象中返回,使用该 property 可以访问/修改该响应式变量值。因为JS中NumberString等基本类型是通过值而非引用传递,这样封装也是为了不同数据类型的行为统一。

  • ref()响应式变量

Number, String使用 ref() ,返回响应式对象,通过value属性访问值。
数组对象也通常使用 ref()

  • 对于对象,使用reactive()toRefs()

    对象使用 reactive() 返回代理对象(响应式)
    为了直接暴露对象的属性名称(来访问值),使得通过该属性名称访问也是响应式的,使用 toRefs() 与对象解构用法:toRefs() 返回一个对象,每个属性值都是原属性的响应式对象 ObjectRefImpl (方法需要一个响应式对象参数)

💡

ref() 返回的是 RefImpl,如果参数是对象,其value就是该对象的代理对象。reactive()直接返回对象的代理对象Proxy。一般对象使用 reactive 更加方便。

// setup()
...
const user = reactive({
      name: 'xiaoming',
      age: 20,
    });
const { name, age } = toRefs(user);
function changName() {
  // console.log(name.value);
  name.value = 'zhou123';
}
function changAge() {
  age.value = 22;
}
return{ name, age, changeName, changeAge}

watch

组合式 API,从 vue 导入watch函数执行相同的操作。

  • 一个想要侦听的响应式引用或 getter 函数

  • 一个回调

  • 可选的配置选项

注意 watch 函数只能监听getter 函数/ ref / reactive 对象,响应式对象中的属性改变会调用回调函数(但不能获取属性旧值)。

另一个是 watchEffect() ,自动分析依赖,发生改变后触发,不能获取旧值。

//setup()
const user = reactive({
	name: 'aaa',
	age: 18,
});
changeUserName(){
	user.name = 'bbb';
}
watch(user, (newVal, oldVal)=>{
	...
});
watchEffect(()=>{
	console.log(user.name);
});

computed

组合式 API,从 vue 导入computed函数执行相同的操作。

返回一个带有 value 属性的对象。模版中可以直接使用该名称

// setup()
const reverseMsg = computed(()=>{
	return msg.value.split('').reverse().join('')
})

setup 获取值

setup() 中不能使用 this,setup(props, context) 有

setup函数中的props也是响应式的,当传入新的 prop 时,它将被更新。为组件接收的参数。因为props是响应式的,不能使用 ES6 解构,它会消除 prop 的响应性,使用toRefs 函数完成。

context是一个普通 JavaScript 对象,暴露了其它可能在setup中有用的值:

  • context.attrs

等同于 $attrs,获取组件标签上定义的属性

  • context.slots

    非响应式对象,等同于 $slots

  • context.emit

    方法,等同于 $emit,用来触发事件

  • context.expose

    函数,暴露公共 property 。如果组件的 setup() 返回渲染函数(替代template),它用来暴露组件的公共 property。组件元素上定义ref="refName",外部组件使用 $refs 来访问(模板 ref)。

生命周期钩子

https://v3.cn.vuejs.org/guide/composition-api-lifecycle-hooks.html

Provide / Inject

setup()中使用provide,导入该方法即可。

setup()中使用inject时,也需要从vue显式导入。

可以在 provide 值时使用refreactive增加值之间的响应性

  • ref 属性

  • reactive 对象

  • 组合式 APIcomputed

// ref
let counter = ref(0)
// 
let user = reactive({
	name: 'abc',
	age: 20,
})
let len = Vue.computed(() => this.todos.length)
provide('counter', counter)
provide('user', user)
provide('len', len)

SFC 单文件组件

<script setup>语法糖,相当于 set() 函数的定义,特殊点:

  • 导入的子模块不需要注册,可以直接在模版中使用

  • 顶层的绑定(变量/函数,导入的函数等)会直接暴露给模版使用(即定义的变量不需要通过 return 暴露)

  • 定义响应式变量,也需要从vue中导入

  • prop 可以使用defineProps()宏来定义

本文链接: Vue 基础

版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

发布日期: 2022-06-09

最新构建: 2024-12-26

本文已被阅读 0 次,该数据仅供参考

欢迎任何与文章内容相关并保持尊重的评论😊 !

共 43 篇文章 | Powered by Gridea | RSS
©2020-2024 Nuo. All rights reserved.